package com.wiken.storeservice.service.impl;

import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wiken.servicecommon.common.Code;
import com.wiken.servicecommon.common.R;
import com.wiken.servicecommon.common.TokenUtil;
import com.wiken.servicecommon.common.exception.DefaultException;
import com.wiken.storeservice.DTO.GeographicLocation;
import com.wiken.storeservice.VO.StoreCategoriesVO;
import com.wiken.storeservice.entity.Store;
import com.wiken.storeservice.entity.StoreType;
import com.wiken.storeservice.entity.Type;
import com.wiken.storeservice.feign.StoreTypeFeignClient;
import com.wiken.storeservice.feign.UserServiceFeignClient;
import com.wiken.storeservice.mapper.StoreMapper;
import com.wiken.storeservice.service.IStoreService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author wiken
 * @since 2022-12-28
 */
@Service
@Transactional
public class StoreServiceImpl extends ServiceImpl<StoreMapper, Store> implements IStoreService {

    @Resource
    StoreTypeFeignClient storeTypeFeignClient;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    UserServiceFeignClient userServiceFeignClient;

//    @Autowired
//    StoreCategoriesServiceImpl storeCategoriesService;

//    @Autowired
//    CommodityServiceImpl commodityService;

    private final String REDIS_STORE_INFO_KEY = "store:";

    //返回false说明没有认证成功，返回true说明有账号认证成功
    public boolean checkAuthorization(Store store, String token) {

        //解析token并取出userId
        Claims claims = TokenUtil.parseToken(token);

        if (token == null) throw new DefaultException(Code.ERROR_AUTHORIZATION, "认证异常");
        if (store.getStorekeeperId() == null) throw new DefaultException(Code.ERROR_AUTHORIZATION, "认证异常");
        if (claims == null) throw new DefaultException(Code.ERROR_AUTHORIZATION, "认证异常");

        String userId = claims.get("userId", String.class);

        //判断token中userid和store中storekeeper中id是否相同
        //如果店主id和AdminId都不相同，返回false
        boolean hasAdmin = userServiceFeignClient.getAdministratorById(userId);
        if (!store.getStorekeeperId().equals(userId) && !hasAdmin)
            return false;

        return true;
    }

    @Override
    public boolean addStore(Store store, String token) {

        if (!checkAuthorization(store, token))
            throw new DefaultException(Code.ERROR_STORE_AUTHORIZATION, "添加店铺异常");


        String id = UUID.randomUUID().toString();
        store.setId(id);
        int success = baseMapper.insert(store);

        StoreType storeType = new StoreType();
        storeType.setStoreId(store.getId());
        storeType.setTypeId(store.getTypeId());
        R result = storeTypeFeignClient.addStoreType(storeType, token);

        if (success <= 0 || result.getCode() != 200) throw new DefaultException(Code.ERROR, "添加店铺失败");

        // TODO 如果店铺添加成功，将这个店铺存储到redis中进行缓存。
        return success > 0;
    }

    @Override
    public boolean updateStore(Store store, String token) {

        if (!checkAuthorization(store, token))
            throw new DefaultException(Code.ERROR_STORE_AUTHORIZATION, "添加店铺异常");

        int success = baseMapper.updateById(store);

        StoreType storeType = new StoreType();
        storeType.setStoreId(store.getId());
        storeType.setTypeId(store.getTypeId());
        R result = storeTypeFeignClient.updateByStoreId(storeType, token);

        return success > 0;
    }

    @Override
    public boolean deleteStore(Store store, String token) {
        if (!checkAuthorization(store, token))
            throw new DefaultException(Code.ERROR_STORE_AUTHORIZATION, "添加店铺异常");

        int success = baseMapper.deleteById(store.getId());

        StoreType storeType = new StoreType();
        storeType.setStoreId(store.getId());
        storeType.setTypeId(store.getTypeId());
        R result = storeTypeFeignClient.delStoreTypeByStoreId(storeType, token);

        if (success <= 0 || result.getCode() != 200) throw new DefaultException(Code.ERROR_STORE_DEL, "店铺删除失败");
        return success > 0;
    }

    @Override
    public List<Store> getStoreByUserId(Store store, String token) {
        String userId = TokenUtil.getUserIdByToken(token);

        List<Store> storeList = baseMapper.selectList(new QueryWrapper<Store>().eq("storekeeper_id", userId));

        return storeList;
    }

    @Override
    public List<Store> getStoreOfNearby(GeographicLocation geographicLocation) {
        List<String> ids = getStoreIdOfNearby(geographicLocation);

        List<Store> stores = baseMapper.selectBatchIds(ids);

        for (Store store :
                stores) {
            R result = storeTypeFeignClient.getTypeByStoreId(store);
            Object typeObj = result.getData().get("type");

            String typeJson = JSON.toJSONString(typeObj);

            Type type = JSON.parseObject(typeJson, Type.class);


            store.setTypeId(type.getId());
        }

        //返回List
        return stores;
    }

    @Override
    public List<Store> getStoreOfNearbyByCategory(String typeId, GeographicLocation geographicLocation) {
        if(typeId == null) throw new DefaultException(Code.ERROR, "类型为空");

        List<Store> storeListOfNearby = getStoreOfNearby(geographicLocation);

        List<Store> storeList = new ArrayList<>();
        for (Store store : storeListOfNearby) {
            if (typeId.equals(store.getTypeId())) storeList.add(store);
        }
        return storeList;
    }

    @Override
    public Page<Store> getStoreList(Page page, String token) {
        String userId = TokenUtil.getUserIdByToken(token);

        Boolean hasAdmin = userServiceFeignClient.getAdministratorById(userId);

        if (!hasAdmin)
            throw new DefaultException(Code.ERROR_AUTHORIZATION, "不是管理员");

        return baseMapper.selectPage(page, null);
    }

    @Override
    public List<String> getStoreIdOfNearby(GeographicLocation geographicLocation) {
        //先查看redis中是否缓存店铺
        GeoOperations<String, Object> geoOperations = redisTemplate.opsForGeo();
        //如果redis中没有缓存店铺就从数据库中读取

        Set<String> keys = redisTemplate.keys("store:*");
        if (keys.size() <= 0) {
            //从数据库中读取
            List<Store> stores = baseMapper.selectList(null);

            //将数据库中读取的数据缓存到redis中

            for (Store store : stores) {
                double longitude = Double.parseDouble(store.getLongitude());
                double latitude = Double.parseDouble(store.getLatitude());
                geoOperations.add("stores", new Point(longitude, latitude), REDIS_STORE_INFO_KEY + store.getId());
            }
        }

        double longitude = Double.parseDouble(geographicLocation.getLongitude());
        double latitude = Double.parseDouble(geographicLocation.getLatitude());

        //附近五km店铺
        Circle circle = new Circle(new Point(longitude, latitude), new Distance(5, RedisGeoCommands.DistanceUnit.KILOMETERS));

        //将redis中店铺所有经纬度与当前地理位置相比较
        GeoResults<RedisGeoCommands.GeoLocation<Object>> nearbyStores = geoOperations.radius("stores", circle);

        List<String> ids = new ArrayList<>();

        for (GeoResult<RedisGeoCommands.GeoLocation<Object>> result : nearbyStores) {
            RedisGeoCommands.GeoLocation<Object> location = result.getContent();

            String id = (String) location.getName();

            id = id.replace(REDIS_STORE_INFO_KEY, "");

            ids.add(id);
        }

        return ids;
    }

    @Override
    public StoreCategoriesVO getStoreById(String storeId) {
        StoreCategoriesVO store = baseMapper.getStoreById(storeId);

        return store;
    }
}
